from fastapi import Depends
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
from jwt import PyJWTError
from starlette.requests import Request

from app.auth.models import Api
from app.core.handlers.auth import decode_token, is_token_revoked
from app.core.handlers.database import SessionBackend, engine
from app.user.models import User

security = HTTPBearer(auto_error=False)


async def state_inject(
    request: Request, credentials: HTTPAuthorizationCredentials = Depends(security)
):
    await assign_db(request)
    await assign_user(request, credentials)
    await assign_api(request)


async def assign_db(request: Request):
    if not hasattr(request.state, "db"):
        request.state.db = SessionBackend(engine)


async def assign_user(request: Request, credentials: HTTPAuthorizationCredentials):
    try:
        # 提取请求中的认证凭证
        request.scope["user"] = None
        if not credentials:
            return

        # 获取数据库会话
        db = request.state.db

        # 解码JWT令牌
        payload = decode_token(credentials.credentials)
        if payload is None:
            return

        # 获取主题(用户ID)
        sub = payload.get("sub")
        if sub is None:
            return

        # 检查黑名单
        jti = payload.get("jti")
        if jti and is_token_revoked(db, jti):
            return
        request.state.jti = jti

    except PyJWTError:
        return

    # 根据用户ID获取用户对象
    user_query = db.select(User, skip_permission_check=True).where(User.id == int(sub), User.is_deleted == False)
    user = db.scalar(user_query)
    # 检查用户是否存在且处于激活状态
    if user is None or not user.is_active:
        return

    request.scope["user"] = user
    if hasattr(request.state.db, "set_user"):
        request.state.db.set_user(user)


async def assign_api(request: Request):
    url_path = request.url.path
    method = request.method
    db = request.state.db
    api_query = db.select(Api, skip_permission_check=True).where(Api.path == url_path, Api.method == method)
    api = db.scalar(api_query)

    request.state.api = api
